package com.ecy.quadratic.request;

import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.webkit.WebSettings;

import com.ecy.quadratic.config.ServerConfig;
import com.ecy.quadratic.reader.ReaderApplication;
import com.ecy.quadratic.request.persistentcookiejar.ClearableCookieJar;
import com.ecy.quadratic.request.persistentcookiejar.PersistentCookieJar;
import com.ecy.quadratic.request.persistentcookiejar.cache.SetCookieCache;
import com.ecy.quadratic.request.persistentcookiejar.persistence.SharedPrefsCookiePersistor;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public abstract class BasicRequest<T> {

    private static final MediaType DEFAULT_TYPE = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");
    public static String METHOD_GET = "get";
    public static String METHOD_POST = "post";
    private HashMap<String, String> mParams;
    private OkHttpClient mClient;
    private ByteArrayOutputStream mHeadImgBos;
    private Handler mUIHandler;

    public BasicRequest(HashMap<String, String> paramsMap) {
        ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(ReaderApplication.getsInstance()));
        mClient = new OkHttpClient().newBuilder()
                .connectTimeout(ServerConfig.CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                .readTimeout(ServerConfig.READ_TIMEOUT, TimeUnit.MILLISECONDS)
                .writeTimeout(ServerConfig.WRITE_TIMEOUT, TimeUnit.MILLISECONDS)
                .cookieJar(cookieJar)
                .build();
        mParams = paramsMap;
        mUIHandler = new Handler(Looper.getMainLooper());
    }

    public BasicRequest(HashMap<String, String> paramsMap, ByteArrayOutputStream byteArrayOutputStream) {
        ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(ReaderApplication.getsInstance()));
        mClient = new OkHttpClient().newBuilder()
                .connectTimeout(ServerConfig.CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                .readTimeout(ServerConfig.READ_TIMEOUT, TimeUnit.MILLISECONDS)
                .writeTimeout(ServerConfig.WRITE_TIMEOUT, TimeUnit.MILLISECONDS)
                .cookieJar(cookieJar)
                .build();
        mHeadImgBos = byteArrayOutputStream;
        mParams = paramsMap;
        mUIHandler = new Handler(Looper.getMainLooper());
    }

    public BasicRequest() {
        mClient = new OkHttpClient().newBuilder()
                .connectTimeout(ServerConfig.CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                .readTimeout(ServerConfig.READ_TIMEOUT, TimeUnit.MILLISECONDS)
                .writeTimeout(ServerConfig.WRITE_TIMEOUT, TimeUnit.MILLISECONDS)
                .build();
    }

    private static String getUserAgent() {
        String userAgent = "";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            try {
                userAgent = WebSettings.getDefaultUserAgent(ReaderApplication.getsInstance());
            } catch (Exception e) {
                userAgent = System.getProperty("http.agent");
            }
        } else {
            userAgent = System.getProperty("http.agent");
        }
        StringBuffer sb = new StringBuffer();
        for (int i = 0, length = userAgent.length(); i < length; i++) {
            char c = userAgent.charAt(i);
            if (c <= '\u001f' || c >= '\u007f') {
                sb.append(String.format("\\u%04x", (int) c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    protected abstract T convert(String result);

    protected abstract String getUrl();

    protected abstract HashMap<String, String> getHeaders();

    protected abstract String method();

    public void setParams(HashMap<String, String> params) {
        mParams = params;
    }

    public void executeImg(final OnResponseListener<T> listener) {

        MultipartBody multipartBody = new MultipartBody.Builder("AaB03x")
                .setType(MultipartBody.FORM)
                .addFormDataPart("imgFile", null, new MultipartBody.Builder("BbC04y")
                        .addPart(Headers.of("Content-Disposition", "form-data; filename=\"img.png\""),
                                RequestBody.create(MediaType.parse("image/png"), mHeadImgBos.toByteArray()))
                        .build())
                .build();
        Request request = addHeaders().url(getUrl()).post(multipartBody).build();
        Call call = mClient.newCall(request);
        call.cancel();
        call.enqueue(new Callback() {

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String responseContent = response.body().string();
                    final T t = convert(responseContent);
                    mUIHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (listener != null) {
                                listener.onSuccess(t, mParams);
                            }
                        }
                    });
                } else {
                    mUIHandler.post(new Runnable() {

                        @Override
                        public void run() {
                            if (listener != null) {
                                listener.onFailed(mParams);
                            }
                        }
                    });
                }
            }

            @Override
            public void onFailure(Call call, IOException arg1) {
                System.out.println(arg1.getMessage());
                mUIHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        listener.onFailed(mParams);
                    }
                });
            }
        });
    }

    public void execute(final OnResponseListener<T> listener) {
        if ("get".equalsIgnoreCase(method())) {
            executeGet(listener);
        } else {
            executePost(listener);
        }
    }

    private void executePost(final OnResponseListener<T> listener) {
        String content = convertParams(mParams);
        RequestBody requestBody = RequestBody.create(DEFAULT_TYPE, content);
        Request request = addHeaders().url(getUrl()).post(requestBody).build();
        Call call = mClient.newCall(request);
        call.enqueue(new Callback() {

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String responseContent = response.body().string();
                    final T t = convert(responseContent);
                    mUIHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (listener != null) {
                                listener.onSuccess(t, mParams);
                            }
                        }
                    });
                } else {
                    mUIHandler.post(new Runnable() {

                        @Override
                        public void run() {
                            if (listener != null) {
                                listener.onFailed(mParams);
                            }
                        }
                    });
                }
            }

            @Override
            public void onFailure(Call call, IOException arg1) {
                mUIHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        listener.onFailed(mParams);
                    }
                });
            }
        });
    }

    private void executeGet(final OnResponseListener<T> listener) {
        String content = convertParams(mParams);
        Request request = addHeaders().url(getUrl() + "?" + content).build();
        Call call = mClient.newCall(request);
        call.enqueue(new Callback() {

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String responseContent = response.body().string();
                    final T t = convert(responseContent);
                    mUIHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (listener != null) {
                                listener.onSuccess(t, mParams);
                            }
                        }
                    });
                } else {
                    mUIHandler.post(new Runnable() {

                        @Override
                        public void run() {
                            if (listener != null) {
                                listener.onFailed(mParams);
                            }
                        }
                    });
                }
            }

            @Override
            public void onFailure(Call call, IOException arg1) {
//				System.out.println(arg1.getMessage());
                mUIHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        listener.onFailed(mParams);
                    }
                });
            }
        });
    }

    private String convertParams(HashMap<String, String> paramsHashMap) {
        StringBuilder tempParams = new StringBuilder();
        try {
            int pos = 0;
            for (String key : paramsHashMap.keySet()) {
                if (pos > 0) {
                    tempParams.append("&");
                }
                tempParams.append(String.format("%s=%s", key, paramsHashMap.get(key) != null ? URLEncoder.encode(paramsHashMap.get(key), "utf-8") : ""));
                pos++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String params = tempParams.toString();
        return params;
    }

    private Request.Builder addHeaders() {
        Request.Builder builder = new Request.Builder();
        HashMap<String, String> headers = getHeaders();
        if (headers != null) {
            Set<Entry<String, String>> headerSet = headers.entrySet();
            Iterator<Entry<String, String>> headerIterator = headerSet.iterator();
            while (headerIterator.hasNext()) {
                Entry<String, String> entry = headerIterator.next();
                builder.addHeader(entry.getKey(), entry.getValue());
            }
        }
        builder.removeHeader("User-Agent").addHeader("User-Agent",
                getUserAgent());
        return builder;
    }

    public interface OnResponseListener<T> {
        void onSuccess(T t, HashMap<String, String> requestParams);

        void onFailed(HashMap<String, String> requestParams);
    }
}
